package masterserver;


import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Logger;

import client.ClientData;
import client.ClientDescription;

import comm.ClientPriority;
import comm.ConnectionType;
import comm.InfrastructureInformation;
import comm.SystemInformation;

import result.Result;
import scheduler.TaskScheduler;
import stats.Stat;
import tasks.CompletedTask;
import tasks.Task;
import tasks.TaskDescription;
import tasks.TaskId;
import tasks.TaskStatus;
import worker.ClassLoaderObjectInputStream;
import worker.WorkerData;



public class MasterServer {

	//	private	LinkedList<LinkedList<TaskDescription>> statefulFaultTolerance = new LinkedList<LinkedList<TaskDescription>>();
	private Hashtable<Long,LinkedList<CompletedTask>> pendingResults = new Hashtable<Long,LinkedList<CompletedTask>>();
	private Hashtable<Long,LinkedList<TaskDescription>> pendingTasks = new Hashtable<Long,LinkedList<TaskDescription>>();

	//private LinkedList<LinkedList<TaskDescription>> pendingTasks = new LinkedList<LinkedList<TaskDescription>>();

	private Hashtable<Long, WorkerData> workerDataVector = new Hashtable<Long, WorkerData>();
	private Hashtable<Long, ClientData> clientDataVector = new Hashtable<Long, ClientData>();

	private Hashtable<Long, WorkerThread> workerThread = new Hashtable<Long, WorkerThread>();
	private Hashtable<Long, ClientThread> clientThread = new Hashtable<Long, ClientThread>();


	private long workerID = 0;

	private TalkToCodeServer talkToCodeServer;
	private final static String className=MasterServer.class.getName();
	private static Logger log=Logger.getLogger(className);


	private long startTime;
	private long clientID=0;
	private static ThreadMXBean tmxb = ManagementFactory.getThreadMXBean();
	private InfrastructureInformation infrastructureInformation;

	private canceledTaskObserver taskObserver = new canceledTaskObserver();


	private TaskScheduler taskScheduler;

	public MasterServer(InfrastructureInformation infrastructureInformation) {
		super();
		this.infrastructureInformation = infrastructureInformation;
		startTime = System.currentTimeMillis();
		System.out.println(tmxb.getCurrentThreadCpuTime());
		System.out.println(tmxb.getCurrentThreadUserTime());
		System.out.println(tmxb.getDaemonThreadCount());
		System.out.println("Version: April 1 - 2011 - Evolve");

	}


	public void execute() {
		connectCodeServer();
		new ReceiveConnectionsThread().start();
	}

	private void connectCodeServer() {

		talkToCodeServer = new TalkToCodeServer(infrastructureInformation.getCodeServerIPaddress(), infrastructureInformation.getCodeServerEncryptedPort());
		talkToCodeServer.connectCodeServer();

	}
	private synchronized long addWorkerData(WorkerData data,WorkerThread wt){
		synchronized (workerDataVector) {
			workerDataVector.put(this.workerID, data);
		}
		synchronized (workerThread) {
			workerThread.put(this.workerID,wt);	
		}

		return workerID++;
	}

	private synchronized long addClient(ClientData cd,ClientThread ct){

		synchronized (clientDataVector) {
			clientDataVector.put(this.clientID, cd);
		}
		synchronized (clientThread) {
			clientThread.put(this.clientID,ct);	
		}
		return clientID++;
	}

	private void removeClient(long id){
		synchronized (clientThread) {
			if(clientThread.containsKey(id))
				clientThread.remove(id);
		}
		synchronized (clientDataVector) {
			if (clientDataVector.containsKey(id))
				clientDataVector.remove(id);

		}
	}

	private  void removeWorker(long id){
		System.out.println("ID to kill:"+id);
		synchronized (workerDataVector) {
			if (workerDataVector.containsKey(id))
				workerDataVector.remove(id);
		}
		synchronized (workerThread) {
			if(workerThread.containsKey(id))
				workerThread.remove(id);
		}

	}


	private void addTaskList(LinkedList<TaskDescription> taskList, long clientID) {
		synchronized (pendingTasks) {
			pendingTasks.put(clientID, taskList);
		}
		System.out.println("Number of lists:"+pendingTasks.size());
		synchronized (this) {
			notifyAll();
		}
	}

	private synchronized void addSingleTask(TaskDescription task) {

		boolean flag = false;
		//addSendedTask_FaultTolerance(task);
		synchronized (pendingTasks) {
			long clientID = task.getClientID();
			if(pendingTasks.containsKey(clientID)){
				LinkedList<TaskDescription> list = pendingTasks.get(clientID);
				synchronized (list) {
					list.add(task);
					//System.out.println(list.size());
				}
				flag = true;
				return;
			}
		}
		if(!flag){
			LinkedList<TaskDescription> localTaskList = new LinkedList<TaskDescription>();
			localTaskList.add(task);
			addTaskList(localTaskList,task.getClientID());
			notifyAll();    
		}

		//System.out.println("counter"+counter++);

	}


	private synchronized TaskDescription getTask(WorkerData workerData) {
		TaskDescription taskToReturn = null;
		long lastClientID = -1;
		int position = -1;

		while (taskToReturn==null){

			try {
				while (pendingTasks.size() == 0) {
					wait();	
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
				System.out.println(getIdAndTime(workerData)
						+ " ->Was interrupted while waiting!");
				return null;
			}

			if (taskScheduler != null){
				if (taskScheduler.dispatchSame()){
					taskToReturn = taskScheduler.getTask();
					lastClientID = taskScheduler.getClientID();
					position = taskScheduler.getPosition();
					return taskToReturn;
				}
				position= taskScheduler.getPosition();

			}
			synchronized (pendingTasks) {
				Long vector[] = new Long[pendingTasks.size()];
				int i = 0;
				Enumeration keys = pendingTasks.keys();
				while (keys.hasMoreElements()) {
					long key = (Long) keys.nextElement();
					LinkedList<TaskDescription> list = pendingTasks.get(key);
					synchronized (list){
						if (list.size() == 0){
							pendingTasks.remove(key);
						}
						else{
							vector[i++] = key;
						}
					}
				}
				if(vector.length>0){
					if(position == -1)
					{
						taskScheduler = new TaskScheduler(pendingTasks.get(vector[0]),0);

					}
					else{
						int new_position = position;
						new_position++;

						if(new_position<vector.length) //Verificar esta altera��o
						{
							//System.out.println("NEW POS:"+new_position);
							//System.out.println("VECTOR SIZE"+vector.length);
							//System.out.println("CLIENT:"+vector[new_position]);
							if(vector[new_position]!=null)
								if(pendingTasks.containsKey(vector[new_position])){
									taskScheduler = new TaskScheduler(pendingTasks.get(vector[new_position]),new_position);
									return taskScheduler.getTask();
								}
						}
						else{
							if(new_position>=vector.length && pendingTasks.size()>0 && vector.length>0) //verificar altera��o 
							{
								taskScheduler = new TaskScheduler(pendingTasks.get(vector[0]),0);
								return taskScheduler.getTask();
							}
						}

					}




				}
			}
		}

		return null;
	}
	private void deleteClientTasks(long clientID) {

		synchronized (pendingTasks) {
			pendingTasks.remove(clientID);
		}
		synchronized (pendingResults) {
			pendingResults.remove(clientID);

		}
	}

	class Checker extends Thread{

		private LinkedList<TaskDescription> taskDescriptions;
		private boolean endChecker;
		private double averageTime;
		private long fastestTask;
		private long slowestTask;
		private int numberOfCompletedTasks;


		public Checker(LinkedList<TaskDescription> taskDescriptions){
			this.taskDescriptions = taskDescriptions;
			this.endChecker = true;
			this.numberOfCompletedTasks = 0;
			this.averageTime = 0;
			this.slowestTask = 0;
			this.fastestTask = Long.MAX_VALUE;

		}

		public void run() {
			while(endChecker){
				synchronized (taskDescriptions) {

					Iterator<TaskDescription> tl = taskDescriptions.iterator();
					while(tl.hasNext()){
						TaskDescription td =(TaskDescription) tl.next();
						if(td.getTaskStatus() == TaskStatus.TERMINATED_OK){
							numberOfCompletedTasks++;
							averageTime+=td.getTimeToComplete();
							if(td.getTimeToComplete()<fastestTask)
								fastestTask = td.getTimeToComplete();
							if(td.getTimeToComplete()>slowestTask)
								slowestTask=td.getTimeToComplete();
							System.out.println("Average time to complete task:"+(averageTime/numberOfCompletedTasks)/1000+" s");
							td.changeTaskStatus(TaskStatus.COMPLETE);

						}
						else 
							if(td.getTaskStatus()==TaskStatus.SCHEDULE){
								long time = System.currentTimeMillis() - td.getStartTime();
								if(time!=0 && time>averageTime){
									//	System.out.println("Superior to Average!! ");
								}
							}
					}

				}
				try {
					Thread.sleep(10000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}
	/*	private void addSendedTask_FaultTolerance(TaskDescription task) {
		boolean flag = false;


		synchronized (statefulFaultTolerance) {
			Iterator<LinkedList<TaskDescription>> taskIterator = statefulFaultTolerance.iterator();
			while(taskIterator.hasNext()){
				LinkedList<TaskDescription> list = (LinkedList<TaskDescription>)taskIterator.next();
				Iterator<TaskDescription> tl = list.iterator();
				while(tl.hasNext()){
					TaskDescription td =(TaskDescription) tl.next();
					if(td.getClientID()==task.getClientID())
					{
						synchronized (list) {
							list.add(task);	
						}
						flag = true;
						break;
					}				
				}
			}
			if(!flag){
				LinkedList<TaskDescription> localTaskList = new LinkedList<TaskDescription>();
				localTaskList.add(task);
				statefulFaultTolerance.add(localTaskList);
				//Checker ch = new Checker(localTaskList);
				//ch.start();
			}
		}
		//System.out.println("FAULT TOLERANCE: Number of tasks:"+pendingTasks.size());
	}

	 */


	private synchronized void addSingleResult(CompletedTask task) {

		synchronized (pendingResults) {
			if (pendingResults.containsKey(task.getTaskDescription().getClientID())){
				LinkedList<CompletedTask> localTaskList = pendingResults.get(task.getTaskDescription().getClientID());
				synchronized (localTaskList) {
					localTaskList.add(task);
					localTaskList.notify();
				}
				return;
			}
			else
			{
				LinkedList<CompletedTask> localTaskList = new LinkedList<CompletedTask>();
				localTaskList.add(task);
				pendingResults.put(task.getTaskDescription().getClientID(), localTaskList);	
				new Deliver(localTaskList,task.getTaskDescription().getClientID()).start();
			}
		}


	}

	private class Deliver extends Thread{

		private LinkedList<CompletedTask> localTaskList;
		private long id;
		private ClientData cd;
		public Deliver(LinkedList<CompletedTask> localTaskList, long id) {
			super();
			this.id = id;
			this.localTaskList =localTaskList;
			synchronized (clientDataVector) {
				if(clientDataVector.containsKey(id))
					cd = clientDataVector.get(id);

			}
		}

		public void run(){

			while(true){	
				synchronized (localTaskList) {
					while(localTaskList.size() == 0 && cd.getTotalNumberOfTasksDone()<cd.getTaskCounter()){
						try {
							localTaskList.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
							return;
						}
					}

					ListIterator<CompletedTask> itr = localTaskList.listIterator();
					while (itr.hasNext()) {
						CompletedTask task = (CompletedTask) itr.next();
						try {
							task.deliverTaskToClient();
							itr.remove();
							cd.addTotalNumberOfTasksDone();

						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
							return;
						}

					}
					if(cd.getTotalNumberOfTasksDone()>=cd.getTaskCounter()){
						synchronized (pendingResults) {
							pendingResults.remove(cd.getId());
						}
						return;
					}
				}


			}
		}

	}


	class canceledTaskObserver extends Observable{
		private ClientDescription clientDescription;

		public void canceledTaskWarning(ClientDescription clientDescription){
			this.clientDescription = clientDescription;

			setChanged();
			notifyObservers(clientDescription);
		}
	}


	private String getIdAndTime(WorkerData workerData) {
		return "(" + workerData.getId() + ") ["
		+ (System.currentTimeMillis() - startTime) + "]";
	}


	class ReceiveConnectionsThread extends Thread {
		public ReceiveConnectionsThread() {

		}

		public void run() {
			ServerSocket serverSocket = null;
			try {
				serverSocket = new ServerSocket(infrastructureInformation.getMasterServerEncryptedPort());
				System.out.println("Waiting for connections on socket: "
						+ serverSocket);
				while (true) {
					try {
						Socket socket = serverSocket.accept();
						socket.setSoTimeout(0);
						socket.setKeepAlive(true);

						ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
						ClassLoaderObjectInputStream in = new ClassLoaderObjectInputStream(socket.getInputStream(),talkToCodeServer);
						ConnectionType type = (ConnectionType) in.readObject();
						if(!(type instanceof ConnectionType)){
							System.out.println("Not what i was expecting... Ignoring...");
							in.close();
							out.close();
							socket.close();
						}
						else{
							switch (type) {
							case CLIENT:
								ClientThread ct = new ClientThread(socket, in, out);
								ct.start();
								break;
							case WORKER:
								WorkerThread wt = new  WorkerThread(socket, in, out);
								taskObserver.addObserver(wt);
								wt.start();
								break;
							case ADMIN:
								AdminThread at = new AdminThread(socket, out, in);
								at.start();
								break;
							default:
								System.out.println("ERROR!");
							}
						}
					} catch (IOException e) {

					} catch (ClassNotFoundException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					if (serverSocket != null)
						serverSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	class AdminThread extends Thread{
		private Socket socket;
		private ObjectOutputStream out;
		private ObjectInputStream in;
		public AdminThread(Socket socket, ObjectOutputStream out,
				ObjectInputStream in) {
			super();
			this.socket = socket;
			this.out = out;
			this.in = in;
		}

		public void run(){
			while(true){
				ConnectionType type;
				try {
					type = (ConnectionType) in.readObject();

					switch (type) {
					case FULL_UPDATE:
						out.writeObject(ConnectionType.FULL_UPDATE);			
						out.writeObject(workerDataVector);
						out.reset();
						out.writeObject(clientDataVector);	
						out.reset();
						break;
					case KILL_WORKER:
						long idWorker = (Long)in.readObject();
						if(workerThread.containsKey(idWorker)){
							WorkerThread wt = workerThread.get(idWorker);
							wt.destroyWorkerSaveTask();
						}

						break;
					case KILL_CLIENT:
						long idClient = (Long)in.readObject();
						if(clientThread.containsKey(idClient)){
							ClientThread ct = clientThread.get(idClient);
							ct.disconnect();
						}
						break;
					default:
						break;
					}

				} catch (IOException e) {
					try {
						in.close();
						out.close();
						socket.close();
						return;
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
						return;
					}
				} catch (ClassNotFoundException e) {
					try {
						in.close();
						out.close();
						socket.close();
						return;
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					e.printStackTrace();
					return;
				}

			}
		}
	}

	class ClientThread extends Thread {
		private Socket socket;
		private ObjectOutputStream out;
		private ClassLoaderObjectInputStream in;
		private int problemNumber;
		private int version;
		private ClientPriority priority;
		private LinkedList<TaskDescription> localTaskList = new LinkedList<TaskDescription>();
		private long myID;
		private ClientDescription description;
		private long taskCounter = 0;
		ClientData cd;
		public ClientThread(Socket socket, ClassLoaderObjectInputStream in,
				ObjectOutputStream out) {
			this.socket = socket;
			this.in = in;
			this.out = out;
			System.out.println("New Client " + socket);

		}

		public void run() {
			try {
				this.cd = new ClientData(socket.getInetAddress().toString(),socket.getPort());
				this.myID = addClient(this.cd,this);
				cd.setId(this.myID);

				while (true) {
					ConnectionType type = (ConnectionType) in.readObject();

					switch (type) {
					case CLIENT_NEW_PROBLEM:
						problemNumber = (Integer) in.readObject();
						version = (Integer) in.readObject();
						in.setProblemAndVersion(problemNumber, version);
						this.priority =  (ClientPriority) in.readObject();
						description = new ClientDescription(myID,priority,out,problemNumber,version);
						cd.setClientPriority(priority);
						cd.setProblemNumber(problemNumber);
						cd.setVersion(version);
						break;
					case CLIENT_NEW_TASK:
						Task task = (Task) in.readObject();
						cd.addTaskCounter();
						addSingleTask(new TaskDescription(task, description,cd.getTaskCounter()));
						break;
					case CLIENT_CANCEL_ALL_TASKS:
						System.out.println(description.toString());
						taskObserver.canceledTaskWarning(description);
						break;
					case START_WORK:
						addTaskList(localTaskList,myID);
						break;
					case CLIENT_NEW_TASK_WAIT:
						Task task2 = (Task) in.readObject();
						localTaskList.add(new TaskDescription(task2, description,taskCounter++));
						break;
					case CLIENT_DISCONNECT:
						deleteClientTasks(myID);
						disconnect();
						break;
					}
				}
			} catch (IOException e) {
				System.out.println("CLIENT: DISCONNECTED OR KILLED:"+this.myID);
				disconnect();
				return;
			} catch (ClassNotFoundException e) {
				System.out.println("CLIENT: DISCONNECTED OR KILLED:"+this.myID);
				disconnect();		
			}
		}

		public void disconnect(){
			try {
				removeClient(myID);
				deleteClientTasks(myID);
				taskObserver.canceledTaskWarning(description);
				in.close();
				out.close();
				socket.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}

	private class WorkerThread extends Thread implements Observer {
		private Socket socket;
		private ObjectOutputStream out;
		private ClassLoaderObjectInputStream in;
		private WorkerData workerData;
		private SystemInformation si;
		private long workerID;
		private LinkedList<TaskDescription> taskList = new LinkedList<TaskDescription>();

		private PingPong pingpong = new PingPong();
		private boolean socketInFlag = false;

		public WorkerThread(Socket socket, ClassLoaderObjectInputStream in,
				ObjectOutputStream out) {
			this.socket = socket;
			this.in = in;
			this.out = out;			
			workerData = new WorkerData();
		}

		public void run() {

			try {

				workerData = (WorkerData)in.readObject();
				workerData.setWorkerAddress(socket.getInetAddress().toString());
				workerData.setWorkerPort(socket.getPort());
				this.workerID = addWorkerData(this.workerData,this);
				workerData.setId(workerID);
				out.writeObject(workerData);
				out.reset();

				pingpong.start();
				ConnectionType type;
				while (true) {
					type = (ConnectionType)in.readObject();
					switch(type){
					case WORKER_FEED:
						new feedWorker().start();
						break;
					case WORKER_RESULTS:
						socketInFlag = true;
						TaskId tid = (TaskId) in.readObject();
						in.setProblemAndVersion(tid.getProblemNumber(), tid.getVersion());
						Result result = (Result) in.readObject();
						WorkerData tempWorkerData = (WorkerData) in.readObject();
						workerData.update(tempWorkerData);
						synchronized (taskList) {
							Iterator<TaskDescription> taskIterator = taskList.iterator();
							synchronized (taskIterator) {
								while(taskIterator.hasNext()){
									TaskDescription task = (TaskDescription)taskIterator.next();
									if(task.getTaskId() == tid.getTaskId()){
										result.setWorkerData(workerData);
										addSingleResult(new CompletedTask(result, task, tid));
										taskIterator.remove();
										//TODO update this line
										//	changeStatusToTask_FaultTolerance(task,TaskStatus.TERMINATED_OK,workerData.getLastTaskTime());
										break;
									}
								}
							}
						}
						socketInFlag = false;
						break;
					case WORKER_CANCELED_TASKS:
						System.out.println("O worker avisou que matou tasks");
						break;

					case WORKER_CANCELED_TASK_OK:
						long taskID = (Long) in.readObject();
						synchronized (taskList) {
							Iterator<TaskDescription> taskIteratorDel = taskList.iterator();
							synchronized (taskIteratorDel) {
								while(taskIteratorDel.hasNext()){
									TaskDescription task = (TaskDescription)taskIteratorDel.next();
									if(task.getTaskId() == taskID){
										taskIteratorDel.remove();
									}
									//		changeStatusToTask_FaultTolerance(task,TaskStatus.FAILED,0);
									else
										System.out.println("not found...");
								}
							}
						}
						break;

					case PONG:
						pingpong.acknolegde();
						break;
					}
				}

			} 
			catch (EOFException e) {
				System.out.println("EX:"+e);
				destroyWorkerSaveTask();
				return;
			}
			catch (IOException e) {
				e.printStackTrace();
				destroyWorkerSaveTask();
				return;
			} catch (ClassNotFoundException e) {

				e.printStackTrace();
				destroyWorkerSaveTask();
				return;

			}


		}
		private class PingPong extends Thread{
			private int flag = 0;

			private final static int TIME_OUT=5000;
			private static final int TIME_TO_SEND_NEXT_PING = 7000;
			public PingPong(){

			}

			public synchronized void waiter(){

				try {
					while(flag==0 && !socketInFlag){
						wait(TIME_OUT);
						if(flag == 0 && !socketInFlag){
							flag = 2;
							return;}
						else
							if(socketInFlag){
								wait();
								flag=1;
								return;
							}
					}
				} catch (InterruptedException e) {
					// 
					e.printStackTrace();
				}
				flag = 1;
			}

			public synchronized void acknolegde(){
				flag = 1;
				notify();

			}

			public void run(){

				while(true){
					try {
						Thread.sleep(TIME_TO_SEND_NEXT_PING);
					} catch (InterruptedException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					synchronized (out) {
						try {
							out.writeObject(ConnectionType.PING);
						} catch (IOException e) {
							destroyWorkerSaveTask();
							return;	
						}
					}
					waiter();
					if(flag==1 || socketInFlag){
						flag = 0;
					}
					else{
						if(flag==2){
							System.out.println("Timeout");
							destroyWorkerSaveTask();
						}
						return;
					}

				}
			}
		}

		public void destroyWorkerSaveTask(){
			try {

				synchronized (taskList) {
					Iterator<TaskDescription> taskIterator = taskList.iterator();
					while(taskIterator.hasNext()){
						TaskDescription task = (TaskDescription)taskIterator.next();
						addSingleTask(task);
						//changeStatusToTask_FaultTolerance(task,TaskStatus.RESCHEDULED,0);
					}
					removeWorker(this.workerID);
				}
				taskList.clear();
				in.close();
				out.close();
				socket.close();
				return;
			} catch (IOException e) {
				try {
					in.close();
					out.close();
					socket.close();
				} catch (IOException e1) {
					return;
				}

			}
		}

		private class feedWorker extends Thread{
			public feedWorker(){

			}
			public void run(){

				TaskDescription taskDescription = getTask(workerData);
				synchronized (taskList) {
					taskList.add(taskDescription);
				}

				try {

					synchronized (out) {
						out.writeObject(ConnectionType.FEED_WORKER);
						out.writeObject(new TaskId(taskDescription.getClientID(),taskDescription.getVersion(),taskDescription.getProblemNumber(),taskDescription.getTaskId()));
						System.out.println("Sending task ... "+taskDescription.getClientID());					
						out.writeObject(taskDescription.getTask());

					}


				} catch (IOException e) {
					e.printStackTrace();
					destroyWorkerSaveTask();
					return;
				}

			}
		}

		@Override
		public void update(Observable o, Object arg) {

			ClientDescription clientDescription = (ClientDescription) arg;


			synchronized (taskList) {
				Iterator<TaskDescription> taskIterator = taskList.iterator();
				synchronized (taskIterator) {

					while(taskIterator.hasNext()){
						TaskDescription task = (TaskDescription)taskIterator.next();
						if(task.getClientID() == clientDescription.getID()){

							synchronized (out) {
								try {
									out.writeObject(ConnectionType.CLIENT_CANCEL_ALL_TASKS);
									out.writeObject(task.getTaskId());
								} catch (IOException e) {
									e.printStackTrace();
									destroyWorkerSaveTask();
									return;
								}

							}
							taskIterator.remove();

						}
					}

				}

			}
		}

	}
}

